﻿using System;
using System.Collections.Generic;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using SmlEngine.UI.Input;
using SmlEngine.UI.Managers;
using SmlEngine.UI.Screens;

namespace SmlEngine.UI.Controls.Menus2
{
    public class Menu : IScene
    {
        #region Constants

        private const float fadeInTime = 0.75f;
        private const float pulseTime = 0.5f;
        private const float fadeOutTime = 0.25f;

        private const float normalScale = 1f;
        private const float selectedScale = 1.25f;

        private static readonly Color normalColor = Color.White;
        private static readonly Color selectedColor = Color.Red;

        #endregion

        #region Fields

        private int selectedIndex;

        private float alpha;
        ScreenState state;

        #endregion

        #region Properties

        #region Timing, Colors and Scales

        public float FadeInTime { get; set; }
        public float PulseTime { get; set; }
        public float FadeOutTime { get; set; }

        public Color NormalColor { get; set; }
        public float NormalScale { get; set; }

        public Color SelectedColor { get; set; }
        public float SelectedScale { get; set; }

        #endregion

        public int OffsetY { get; set; }

        public SpriteFont Font { get; set; }

        public int SelectedIndex
        {
            get { return selectedIndex; }
            set { selectedIndex = value; }
        }

        public IList<MenuEntry> MenuEntries { get; private set; }

        public Action<Menu> Cancelled { get; set; }

        public Screen Screen { get; set; }

        #endregion

        #region Methods

        public Menu(Screen screen)
        {
            FadeInTime = fadeInTime;
            PulseTime = pulseTime;
            FadeOutTime = fadeOutTime;

            NormalScale = normalScale;
            SelectedScale = selectedScale;

            NormalColor = normalColor;
            SelectedColor = selectedColor;

            MenuEntries = new List<MenuEntry>(8);
            Screen = screen;

            Font = Screen.ScreenManager.DefaultFont;

            state = ScreenState.TransitionOn;
        }

        public void Cancel()
        {
            Cancelled(this);
        }

        public void Select()
        {
            MenuEntries[selectedIndex].Select();
        }

        public void ReloadContent() { }
        #region Update and Draw

        private int GetWidth()
        {
            return Area.HasValue ? Area.Value.Width : Screen.ScreenManager.Width;
        }

        private Rectangle GetArea()
        {
            return Area.HasValue ?
                Area.Value : new Rectangle(0, 0, Screen.ScreenManager.Width, Screen.ScreenManager.Height);
        }

        public void Update(GameTime gameTime, InputManager input)
        {
            if (input.IsNewKeyMapPress(InputSettings.Up))
            {
                selectedIndex--;

                if (selectedIndex < 0)
                    selectedIndex = MenuEntries.Count - 1;
            }
            else if (input.IsNewKeyMapPress(InputSettings.Down))
            {
                selectedIndex++;

                if (selectedIndex >= MenuEntries.Count)
                    selectedIndex = 0;
            }

            if (input.IsNewKeyMapPress(InputSettings.Left))
            {
                MenuEntries[selectedIndex].Left();
            }
            else if (input.IsNewKeyMapPress(InputSettings.Right))
            {
                MenuEntries[selectedIndex].Right();
            }

            if (input.IsNewKeyMapPress(InputSettings.Select))
            {
                Select();
            }
            else if (input.IsNewKeyMapPress(InputSettings.Cancel))
            {
                Cancel();
            }

            if (state == ScreenState.TransitionOn)
            {
                alpha += gameTime.GetElapsedSeconds() / FadeInTime;

                if (alpha >= 1f)
                {
                    alpha = 1f;
                    state = ScreenState.Active;
                }
            }
            else if (state == ScreenState.TransitionOff)
            {
                alpha -= gameTime.GetElapsedSeconds() / FadeInTime;

                if (alpha <= 0f)
                {
                    alpha = 0f;
                    state = ScreenState.Hidden;
                    Screen.Scenes.Remove(this);
                }
            }
        }

        public void UpdateEntries()
        {
            // Make the menu slide into place during transitions, using a
            // power curve to slow down as it nears the end.
            float transitionOffset = (float)Math.Pow(1 - alpha, 2);

            Vector2 position = new Vector2(0, OffsetY);

            for (int i = 0; i < MenuEntries.Count; i++)
            {
                var entry = MenuEntries[i];

                // Center the entries.
                position.X = GetWidth() / 2 - entry.GetWidth(Font) / 2;

                if (state == ScreenState.TransitionOn)
                    position.X += transitionOffset * 400;
                else if (state == ScreenState.TransitionOff)
                    position.X -= transitionOffset * 200;

                entry.CurrentPosition = position;

                position.Y += entry.GetHeight(Font);

                if (i == selectedIndex)
                    entry.Transition(true, true);
                else
                    entry.Transition(false, false);
            }
        }

        public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
        {
            UpdateEntries();

            for (int i = 0; i < MenuEntries.Count; i++)
                MenuEntries[i].Draw(spriteBatch, Font, alpha);
        }

        #endregion

        #endregion

        #region Virtual IScene Members

        public virtual Rectangle? Area { get; set; }

        public virtual void Initialize() { }

        public virtual bool IsDebugging { get; set; }

        public virtual void Debug()
        {
            IsDebugging = true;
        }

        #endregion
    }
}
